using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Comteck.Winforms.Infrastructure.ToExcel.Attributes;
using DevExpress.Utils;
using NPOI.SS.UserModel;
using NPOI.XSSF.Streaming;
using DictionaryExtensions = DevExpress.Mvvm.Native.DictionaryExtensions;

namespace Comteck.Winforms.Infrastructure.ToExcel.Converter {
  /// <summary>
  /// 这里指定以.xlsx扩展名的Excel
  /// <see cref="https://gitee.com/godenSpirit/mto-excel/blob/master/MToExcel/Converter/BasicConverter.cs"/>
  /// </summary>
  public class BasicConvert : ITConvert {
    public IWorkbook ConvertToExcel<T>(List<T> list) {
      IWorkbook workbook = new SXSSFWorkbook();
      var defaultSheet = workbook.CreateSheet("SheetOne");
      // 获取传递的泛型类型
      var type = typeof(T);

      // 首先判断泛型T是否为基础数据类型
      // 如果泛型类型为基础数据类型，则为写一行数据
      if (IsBasicType(type)) {
        var uniqueRow = defaultSheet.CreateRow(0);
        var count = 0;

        list.ForEach(item => {
          uniqueRow.CreateCell(count++).SetCellValue(Convert.ToString(item));
        });

        return workbook;
      }

      // 如果不是基础数据类型就反射获取其属性写入Excel
      var properties = type.GetProperties();
      // 先用属性名打印一行表头
      var header = defaultSheet.CreateRow(0);
      // 设置一下表头样式，将表头设置为加粗字体
      var style = workbook.CreateCellStyle();
      var font = workbook.CreateFont();
      font.IsBold = true;
      style.SetFont(font);

      var columnIndex = 0; // 列号

      // 打印标题行
      foreach (var property in properties) {
        // 如果在忽略类型中就直接跳过，开始下一轮循环
        if (WrapperConverter.IgnoreTypePool.Contains(
          new KeyValuePair<Type, IgnoreTypeAttribute>(property.PropertyType,
            (IgnoreTypeAttribute) property.GetCustomAttribute(typeof(IgnoreTypeAttribute)) == null
              ? new IgnoreTypeAttribute(false)
              : (IgnoreTypeAttribute) property.GetCustomAttribute(typeof(IgnoreTypeAttribute))))) {
          continue;
        }

        // 打印标题
        if (WrapperConverter.CustomNamePool.Contains(
          new KeyValuePair<Type, HeaderNameAttribute>(property.PropertyType,
            (HeaderNameAttribute) property.GetCustomAttribute(typeof(HeaderNameAttribute)) == null
              ? new HeaderNameAttribute("")
              : (HeaderNameAttribute) property.GetCustomAttribute(typeof(HeaderNameAttribute))))) {
          var name = (HeaderNameAttribute) property.GetCustomAttribute(typeof(HeaderNameAttribute));
          header.CreateCell(columnIndex).SetCellValue(name.HeaderName);
          header.GetCell(columnIndex).CellStyle = style;
          columnIndex++;
          continue;
        }

        if (WrapperConverter.ReferenceTypePool.Contains(
          new KeyValuePair<Type, ReferenceTypeAttribute>(property.PropertyType,
            (ReferenceTypeAttribute) property.GetCustomAttribute(typeof(ReferenceTypeAttribute)) == null
              ? new ReferenceTypeAttribute()
              : (ReferenceTypeAttribute) property.GetCustomAttribute(typeof(ReferenceTypeAttribute))))) {
          var referType = (ReferenceTypeAttribute) property.GetCustomAttribute(typeof(ReferenceTypeAttribute));
          // 判断是否要将引用类型拆分成多列
          if (referType.IsMultiColumn) {
            var subProperties = property.PropertyType.GetProperties();

            // PropertyInfo.PropertyType 可以属性的Type信息
            // PropertyInfo.DeclaredType 可以取出定义这个属性的类型信息
            // 再将属性类型的属性全部取出
            foreach (var pi in subProperties.Where(x => x.DeclaringType != null)) {
              header.CreateCell(columnIndex).SetCellValue(Convert.ToString(pi.DeclaringType.Name + ":" + pi.Name));
              header.GetCell(columnIndex).CellStyle = style;
              columnIndex++;
            }

            continue;
          }

          header.CreateCell(columnIndex).SetCellValue(Convert.ToString(property.Name));
          continue;
        }

        header.CreateCell(columnIndex).SetCellValue(property.Name);
        header.GetCell(columnIndex).CellStyle = style;
        columnIndex++;
      }

      var rowNumber = 1; // 行号
      list.ForEach(item => {
        // 逐行创建数据
        var row = defaultSheet.CreateRow(rowNumber);
        properties = item.GetType().GetProperties();
        columnIndex = 0; // 列号

        // 忽略字段直接跳过
        foreach (var property in properties) {
          if (WrapperConverter.IgnoreTypePool.Contains(
            new KeyValuePair<Type, IgnoreTypeAttribute>(property.PropertyType,
              (IgnoreTypeAttribute) property.GetCustomAttribute(typeof(IgnoreTypeAttribute)) == null
                ? new IgnoreTypeAttribute(false)
                : (IgnoreTypeAttribute) property.GetCustomAttribute(typeof(IgnoreTypeAttribute))))) {
            continue;
          }

          if (WrapperConverter.ReferenceTypePool.Contains(
            new KeyValuePair<Type, ReferenceTypeAttribute>(property.PropertyType,
              (ReferenceTypeAttribute) property.GetCustomAttribute(typeof(ReferenceTypeAttribute)) == null
                ? new ReferenceTypeAttribute()
                : (ReferenceTypeAttribute) property.GetCustomAttribute(typeof(ReferenceTypeAttribute))))) {
            var refer = DictionaryExtensions.GetValueOrDefault(WrapperConverter.ReferenceTypePool, property.PropertyType);
            if (refer.IsMultiColumn) {
              var pros = property.PropertyType.GetProperties();
              // 单列的话，直接都追加到一列里去
              var appending = "";

              foreach (var info in pros) {
                if (info.GetValue(info.GetValue(item)) == null) {
                  appending += "空置属性|";
                } else {
                  appending += Convert.ToString(info.GetValue(property.GetValue(item))) + ":";
                }
              }

              row.CreateCell(columnIndex).SetCellValue(appending);
            }
            // 同样在打印引用类型的属性完成后，需要跳一下循环，防止再打印一遍全限定名
            continue;
          }
          
          if (property.GetValue(item) == null) { // 在这里进行属性判空
            row.CreateCell(columnIndex).SetCellValue("空值属性");
            columnIndex++;
          } else if (IsBasicArrayType(property.GetValue(item).GetType())) { // 判断属性的类型是否为基础数据类型数组，这里先把数组的内容全写到一个格子中
            // 试一下是否能在反射中将属性值强制转化为数组，这里将所有数组的数据都写到一格数据中，以后应该提供更多的方式
            row.CreateCell(columnIndex).SetCellValue("");

            var appending = "";
            // 将数组属性转化为Array类型进行遍历
            var unknownArray = (Array) property.GetValue(item);

            for (var i = 0; i < unknownArray.Length; i++) {
              appending += Convert.ToString(unknownArray.GetValue(i)) + ",";
            }

            row.CreateCell(columnIndex).SetCellValue(appending);
          } else { // 打印剩下的属性类型是基础数据类型的情况
            row.CreateCell(columnIndex).SetCellValue(Convert.ToString(property.GetValue(item)));
            columnIndex++;
          }
        }

        rowNumber++;
      });

      return workbook;
    }

    /// <summary>
    /// 判断一个类型是否为基础数据类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns>true=是，false=否</returns>
    private static bool IsBasicType(Type type) {
      return type != null && (type == typeof(int)
                              || type == typeof(double)
                              || type == typeof(float)
                              || type == typeof(bool)
                              || type == typeof(string)
                              || type == typeof(byte)
                              || type == typeof(char)
                              || type == typeof(long)
                              || type == typeof(DateTime)
                              || type == typeof(decimal));
    }

    /// <summary>
    /// 判断类型是否为基础数据类型的数组
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static bool IsBasicArrayType(Type type) {
      return type != null && (type == typeof(int[])
                              || type == typeof(double[])
                              || type == typeof(float[])
                              || type == typeof(bool[])
                              || type == typeof(string[])
                              || type == typeof(byte[])
                              || type == typeof(char[])
                              || type == typeof(long[])
                              || type == typeof(DateTime[])
                              || type == typeof(decimal[]));
    }
  }
}
